#include "tcp_server.h"
#include "../coro/task.h"
#include "../sys/thread_pool.h"
#include "../util/logger.h"
#include "../util/assert.h"
#include <signal.h>

namespace wilson::net
{
    static auto& logger = ::log4cpp::Category::getRoot();

    tcp_server::tcp_server(coro::epoll_suspend_manager& esm,
                           ipv4_address addr, in_port_t port, 
                           tcp_task_callback_t cb)
        : ts_suspend_mgr{ esm }, 
          ts_sock{ ::std::move(addr), port },
          ts_callback{ ::std::move(cb) }, 
          ts_listener{ [](socket& sock) { 
              sock.reuse_addr(); 
              sock.reuse_port(); 
              sock.bind_as_tcp();
              return listener{ sock };
          }(ts_sock)}
    {
        ::signal(SIGPIPE, SIG_IGN);
    }

    void tcp_server::start()
    {
        WILSON_ASSERT(ts_callback != nullptr);
        schedule(ts_suspend_mgr.task_manager(), listen_loop());
    }

    void tcp_server::stop()
    {
        ts_stop_src.request_stop();
        ts_sock.close();
    }

    void tcp_server::join()
    {
        ts_join_sem.acquire();
    }

    coro::task<void> tcp_server::listen_loop()
    {
        auto stp_token = ts_stop_src.get_token();
        while (!stp_token.stop_requested())
        try
        {
            auto conn = co_await ts_listener.coro_accept(ts_suspend_mgr);
            schedule(
                ts_suspend_mgr.task_manager(), 
                ts_callback(tcp_server_context{ ::std::move(conn) })
            ); 
        }
        catch (...)
        {
            logger.debug("sock has been closed");
        }
        ts_join_sem.release();
        co_return;
    }
}
